﻿using System;
using System.Collections.Generic;
using System.Threading;

namespace Mbs.Utils.ThreadPool
{
    /// <summary>
    /// 线程池类
    /// </summary>
    public class ThreadPoolExecutor
    {
        /// <summary>
        /// 核心线程的任务队列
        /// </summary>
        private readonly Queue<WorkTask> tasks = new Queue<WorkTask>();
        /// <summary>
        /// 最大核心线程数
        /// </summary>
        private int coreThreadCount;
        /// <summary>
        /// 最大非核心线程数
        /// </summary>
        private int noneCoreThreadCount;
        /// <summary>
        /// 当前运行的核心线程的数量
        /// </summary>
        private int runCoreThreadCount;
        /// <summary>
        /// 当前运行的非核心线程的数量
        /// </summary>
        private int runNoneCoreThreadCount;
        /// <summary>
        /// 核心线程队列的最大数
        /// </summary>
        private int maxQueueCount;
        /// <summary>
        /// 当核心线程空闲时最大活跃时间
        /// </summary>
        private int keepAliveTimeout;
        /// <summary>
        /// 设置是否为后台线程
        /// </summary>
        private bool isBackground;
        private ThreadPoolExecutor() { }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="CoreThreadCount">核心线程数</param>
        /// <param name="TotalThreadCount">总线程数</param>
        /// <param name="IsBackground">是否为后台线程</param>
        /// <param name="QueueCount">核心队列的最大数</param>
        /// <param name="KeepAliveTimeout">当核心线程空闲时最大活跃时间</param>
        /// <exception cref="ArgumentOutOfRangeException"></exception>
        /// <exception cref="ArgumentException"></exception>
        public ThreadPoolExecutor(int CoreThreadCount = 5, int TotalThreadCount = 10, bool IsBackground = true, int QueueCount = 200, int KeepAliveTimeout = 0)
        {
            if (CoreThreadCount < 1) throw new ArgumentOutOfRangeException(nameof(CoreThreadCount), CoreThreadCount, null);
            if (TotalThreadCount < CoreThreadCount) throw new ArgumentException($"{nameof(TotalThreadCount)}:{TotalThreadCount} must be greater than {nameof(CoreThreadCount)}:{CoreThreadCount}");
            if (QueueCount < 0) throw new ArgumentOutOfRangeException(nameof(QueueCount), QueueCount, null);
            if (KeepAliveTimeout < 0) throw new ArgumentOutOfRangeException(nameof(KeepAliveTimeout), KeepAliveTimeout, null);
            coreThreadCount = CoreThreadCount;
            noneCoreThreadCount = TotalThreadCount - CoreThreadCount;
            keepAliveTimeout = KeepAliveTimeout;
            maxQueueCount = QueueCount;
            isBackground = IsBackground;
        }
        /// <summary>
        /// 执行任务
        /// </summary>
        /// <param name="task">一个自定义任务</param>
        /// <exception cref="ArgumentNullException">任务为null时，抛出该错误</exception>
        /// <exception cref="NotSupportedException">当核心任务队列已满且非核心线程最大数为0时抛出该错误</exception>
        public void QueueTask(WorkTask task)
        {
            if (task == null) throw new ArgumentNullException(nameof(task));
            lock (tasks)
            {
                tasks.Enqueue(task);
                if (tasks.Count <= maxQueueCount)
                {
                    if (runCoreThreadCount < coreThreadCount)
                    {
                        ++runCoreThreadCount;
                        Run(true);
                    }
                }
                else
                {
                    if (noneCoreThreadCount > 0 && runNoneCoreThreadCount < noneCoreThreadCount)
                    {
                        ++runNoneCoreThreadCount;
                        Run(false);
                    }
                }
            }
        }

        private void Run(bool isCore)
        {
            Tuple<int, bool> state = new(keepAliveTimeout, isCore);
            Thread thread = new(t => Excute(t))
            {
                Name = Guid.NewGuid().ToString("D"),
                IsBackground = isBackground
            };
            thread.Start(state);
            
        }

        private void Excute(object? state)
        {
            if (state == null) return;
            var parameter = (Tuple<int, bool>)state;
            bool first = true;
            DateTime firstTime = DateTime.Now;
            while (true)
            {
                WorkTask? item = null;
                lock (tasks)
                {
                    if (tasks.Count > 0)
                    {
                        first = true;
                        item = tasks.Dequeue();
                    }
                    else
                    {
                        if (parameter.Item2)
                        {

                            if (first)
                            {
                                firstTime = DateTime.Now;
                                first = false;
                            }
                            if ((DateTime.Now - firstTime).TotalMilliseconds > parameter.Item1)
                            {
                                --runCoreThreadCount;
                                break;
                            }
                        }
                        else
                        {
                            --runNoneCoreThreadCount;
                            break;
                        }
                    }
                }
                item?.Runsynchronous();
            }
        }

        public void CanceleAll()
        {
            lock (tasks)
            {
                tasks.Clear();
            }
        }
    }
}